package com.xo.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xo.common.constant.RedisConfigConstant;
import io.lettuce.core.ClientOptions;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.config.TransportMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
public class RedisConfig {
    //todo 待优化
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // 1. 配置Redis单机连接信息
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        // 设置Redis服务器主机地址（IP或域名）
        config.setHostName(RedisConfigConstant.HOST);
        // 设置Redis服务器端口号（默认6379）
        config.setPort(RedisConfigConstant.PORT);
        // 设置Redis访问密码（如果Redis服务器配置了密码验证）
        config.setPassword(RedisPassword.of(RedisConfigConstant.PASSWORD));
        // 设置要连接的Redis数据库编号（默认0，Redis支持多数据库逻辑隔离）
        config.setDatabase(RedisConfigConstant.DATABASE);

        // 2. 配置Lettuce客户端的高级特性
        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                // 设置命令执行超时时间（2秒），超过此时长未响应则抛出超时异常
                .commandTimeout(Duration.ofSeconds(2))
                // 设置客户端关闭超时时间（100毫秒），确保资源释放时有足够时间完成清理
                .shutdownTimeout(Duration.ofMillis(100))
                // 配置客户端行为选项
                .clientOptions(ClientOptions.builder()
                        // 开启自动重连机制：连接断开后自动尝试重新连接
                        .autoReconnect(true)
                        // 配置断开连接时的行为：拒绝所有命令执行，避免发送无效请求
                        .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                        .build())
                .build();

        // 3. 创建并返回Lettuce连接工厂，整合连接信息和客户端配置
        // LettuceConnectionFactory是Spring Data Redis对Lettuce客户端的封装
        return new LettuceConnectionFactory(config, clientConfig);
    }


    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建RedisTemplate实例，指定键为String类型，值为Object类型
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置Redis连接工厂（依赖Lettuce连接池配置）
        template.setConnectionFactory(factory);

        // 1. 配置Jackson序列化器（核心安全优化）
        // 创建ObjectMapper用于自定义JSON序列化规则
        ObjectMapper objectMapper = new ObjectMapper();
        // 设置属性可见性：允许访问所有字段（包括private），确保对象所有属性能被序列化
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        // 关键安全配置：禁用默认类型信息写入
        // 注释原因：默认情况下会在JSON中添加@class字段存储类全路径，可能导致：
        // 1. 泄露类结构信息 2. 存在反序列化安全漏洞（如远程代码执行风险）
        // objectMapper.activateDefaultTyping(LazyParsingMapper.DefaultTyping.NON_FINAL);

        // 2. 初始化自定义Jackson序列化器
        // 关联自定义的ObjectMapper，指定序列化目标类型为Object
        Jackson2JsonRedisSerializer<Object> serializer =
                new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);

        // 3. 配置字符串序列化器（用于键的序列化）
        StringRedisSerializer stringSerializer = new StringRedisSerializer();

        // 4. 为RedisTemplate设置序列化器
        template.setKeySerializer(stringSerializer);         // 普通键使用字符串序列化
        template.setHashKeySerializer(stringSerializer);     // Hash结构的键使用字符串序列化
        template.setValueSerializer(serializer);             // 普通值使用Jackson序列化
        template.setHashValueSerializer(serializer);         // Hash结构的值使用Jackson序列化

        // 5. 初始化模板配置（必须调用，确保所有序列化器生效）
        template.afterPropertiesSet();
        return template;
    }



    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.setTransportMode(TransportMode.NIO);

        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress(RedisConfigConstant.REDISSON_URL);
        singleServerConfig.setPassword(RedisConfigConstant.PASSWORD);
        singleServerConfig.setDatabase(RedisConfigConstant.DATABASE);

        // 连接池配置
        singleServerConfig.setConnectionPoolSize(64);          // 最大连接数
        singleServerConfig.setConnectionMinimumIdleSize(24);   // 最小空闲连接
        singleServerConfig.setSubscriptionConnectionPoolSize(50); // 订阅连接池
        singleServerConfig.setIdleConnectionTimeout(10000);    // 空闲连接超时
        singleServerConfig.setConnectTimeout(10000);           // 连接超时
        singleServerConfig.setTimeout(3000);                   // 命令超时
        singleServerConfig.setRetryAttempts(3);                // 重试次数
        singleServerConfig.setRetryInterval(1500);             // 重试间隔

        return Redisson.create(config);
    }




}
